package com.ewei.web.common.utils;

import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.io.Writer;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;

import javax.imageio.ImageIO;

import org.apache.commons.io.FileExistsException;
import org.apache.commons.io.FileUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.ewei.web.common.exception.MessageRuntimeException;
import com.ewei.web.common.io.BufferedRandomAccessFile;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;

/**
 * 文件工具包(结合springMVC
 * @author David
 *
 */
public class FileToolsBar {
	private static final Logger logger = LogManager.getLogger(FileToolsBar.class);
	//private static final String DEFAULT_CHARSET = "utf-8";//默认编码
	private static final String DEFAULT_UPLOAD_DIR = "/";//默认上传目录
	//默认缓冲区大小(Buffer)
	private static final int DEFAULT_BUFFER_SIZE = 1024*64;
	
	private FileToolsBar(){
	}
	/**
	 * 复制目录
	 * @param sourcePath 源地址 
	 * @param destPath 目标地址
	 */
	public static void  copyDirectory(String srcPath,String destPath){
		File source = new File(srcPath);
		File dest = new File(destPath);
		copyDirectory(source,dest);
	}
	public static void copyDirectory(File src,File dest){
		if(dest.isDirectory()&&src.isDirectory()){
			try {
				FileUtils.copyDirectory(src, dest);
			} catch (IOException e) {
				logger.info(e);
				throw new MessageRuntimeException(e);
			} catch(NullPointerException ne){
				logger.info(ne);
				throw new IllegalArgumentException("the source and destination  must not be null");
			}
		}else{
			throw new IllegalArgumentException("the source and destination  must be a direcotry");
		}
	}
	/**
	 * 删除文件或者目录
	 * @param path 路径
	 */
	public static void deleteFileOrDirectory(String path){
		File file = new File(path);
		if(!file.exists()){
			throw new IllegalArgumentException("the file or directory deleted is empty!"); 
		}
		FileUtils.deleteQuietly(file);//内部自动处理异常
	}
	/**
	 * 移动目录
	 * @param srcPath 源地址
	 * @param destPath 目标地址
 	 */
	public static void moveDirectory(String srcPath,String destPath){
		File src = new File(srcPath);
		File dest = new File(destPath);
		moveDirectory(src, dest);
	}
	public static void moveDirectory(File src,File dest){
		if(dest.isDirectory()){
			try {
				FileUtils.moveDirectory(src, dest);
			}catch(FileExistsException fe){
				logger.info(fe);
				throw new IllegalArgumentException("the destinatiion already exist");
			} catch (IOException e) {
				logger.info(e);
				throw new MessageRuntimeException(e);
			}catch(NullPointerException ne){
				logger.info(ne);
				throw new IllegalArgumentException("the source must not be null");
			}
		}else{
			throw new IllegalArgumentException("the source and destination must be a direcotry");
		}
	}
	/**
	 * 重命名文件
	 * @param file
	 * @param newName
	 */
	public static void renameToFile(File file,String newName){
		String oldPath = file.getAbsolutePath();
		oldPath = PathUtils.format(oldPath);//格式化路径
		String newPath = getNewPath(oldPath,newName);
		File newFile = new File(newPath);
		file.renameTo(newFile);
	}
	/**
	 * 读取文件或者目录大小
	 * @param path
	 * @return
	 */
	public static Long getSize(String path){
		File file  = new File(path);
		return getSize(file);
	}
	public static Long getSize(File file){
		try{
			if(file.isDirectory()){
				return FileUtils.sizeOfDirectory(file);
			}else{ 
				return FileUtils.sizeOf(file);
			}
		}catch(NullPointerException ne){
			logger.info(ne);
			throw new IllegalArgumentException("the file is null");
		}
	}
	/**
	 * 获得路径下的文件列表
	 * @param path
	 * @return
	 */
	public static String[] fileList(String path,String[] excludeTypes ){
		return fileList(new File(path),excludeTypes);
	}
	public static String[] fileList(File file,final String[] excludeTypes){
		return  file.list(new FilenameFilter() {
			@Override
			public boolean accept(File dir, String name) {
				boolean bool = true;
				if(null!=excludeTypes){
					for(String type:excludeTypes){
						if(name.endsWith(type)){
							bool = false;
							break;
						}
					}
				}
				return bool;
			}
		});
	}
	/**
	 * 从文件中读字符写入字符流
	 * (读字符使用带缓冲的随机流)
	 * @param file
	 * @param writer
	 */
	public static void readFileIntoHtml(File file,Writer writer){
		try{
			BufferedRandomAccessFile access = new BufferedRandomAccessFile(file, "r");
			String str = null;
			while((str = access.readBufferLine())!=null){
				writer.write(str+"<br/>");
			}
			access.close();
		}catch(Exception e){
			logger.info(e);
			throw new MessageRuntimeException("read file failed!");
		}
	}
	public static void readFileIntoStream(File file,Writer writer){
		try{
			BufferedRandomAccessFile access = new BufferedRandomAccessFile(file, "r");
			String str = null;
			while((str = access.readBufferLine())!=null){
				writer.write(str);
			}
			access.close();
		}catch(Exception e){
			logger.info(e);
			throw new MessageRuntimeException("read file failed!");
		}
	}
	/**
	 * 从文件中读字节写入字节流
	 * (读字节使用带缓冲的随机流)
	 * @param file
	 * @param writer
	 */
	public static void readFileIntoStream(File file,OutputStream out){
		try{
			BufferedRandomAccessFile access = new BufferedRandomAccessFile(file, "r");
			int len = -1;
			byte[] b = new byte[1024];
			while((len = access.readBuffer(b))>0){
				out.write(b, 0, len);
			}
			access.close();
		}catch(Exception e){
			e.printStackTrace();
			logger.info(e);
			throw new MessageRuntimeException("read file failed!");
		}
	}
	/**
	 * 转移文件
	 * @param file 
	 * @param destDir 目标地址（默认值）
	 */
	public static void transferFile(File file,String dest){
		String destDir = DEFAULT_UPLOAD_DIR;
		if(null != dest){
			destDir = dest;
		}
		SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
		String dir2 = formatter.format(new Date()); //根据时间创建子目录
		String newName = UUID.randomUUID().toString().replace("-","");
		destDir += dir2+"/"+newName+getExt(file.getPath());
		File destFile = new File(destDir);
		try {
			FileUtils.copyFile(file, destFile);
		} catch (IOException e) {
			logger.info(e);
			throw new MessageRuntimeException(e);
		}
	}
	/**
	 * 网络上传小文件(NIO)
	 * @param in
	 * @param destPath
	 * @param size
	 * @throws IOException
	 */
	public static void uploadsf(InputStream in,String destPath,int size) throws IOException{
		ReadableByteChannel cf = Channels.newChannel(in);
		RandomAccessFile destFile = new RandomAccessFile(destPath,"rw");
		FileChannel ct = destFile.getChannel();
		int buffer = DEFAULT_BUFFER_SIZE;
		if(size>0){
			buffer = size;
		}
		long num = 0;
		long position = 0;
		do{
			num = ct.transferFrom(cf, position, buffer);
			position +=num;
		}while(num>0);
		ct.close();
		cf.close();
		destFile.close();
	}
	/**
	 * 网络上传小文件(NIO+DirectBuffer)
	 * @param in
	 * @param destPath
	 * @param buffer
	 * @throws IOException
	 */
	public static void uploadsf2(InputStream in,String destPath,int size) throws IOException{
		ReadableByteChannel cf = Channels.newChannel(in);
		RandomAccessFile destFile = new RandomAccessFile(destPath,"rw");
		FileChannel ct = destFile.getChannel();
		int buffer = DEFAULT_BUFFER_SIZE;
		if(size>0){
			buffer = size;
		}
		ByteBuffer b = ByteBuffer.allocateDirect(buffer);
		int remain = 0;
		do{
			remain = cf.read(b);
			ct.write(b);
		}while(remain>0);
		ct.close();
		cf.close();
		destFile.close();
		//release the direct memory
		release(b);
	}
	/**
	 * 压缩图片文件
	 * @param in
	 * @param location
	 * @param width
	 * @param height 
	 * @param propertion 是否等比例缩放
	 */
	public static void compressPic(File fIn,String location,int width,int height,boolean propertion){
		try{
			long current0 = System.currentTimeMillis();
			Image img = ImageIO.read(fIn);//从流中读取img图片
			int oldwidth = img.getWidth(null);
			int oldheight = img.getHeight(null);
			if(propertion){//对等比例缩放进行处理
				double rate0 = (double)oldwidth/(double)width;
				double rate1 = (double)oldheight/(double)height;
				double rate = rate0<rate1?rate0:rate1;//取用最小的缩放比例
				width = (int)(oldwidth/rate);
				height = (int)(oldheight/rate);
			}
			BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
			image.getGraphics().
				drawImage(img.getScaledInstance(width, height, Image.SCALE_SMOOTH), 0, 0, null);//在画板上描绘
			System.out.println(System.currentTimeMillis()-current0);
			FileOutputStream out = new FileOutputStream(location);
			JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
			encoder.encode(image);
			out.close();
		}catch(IOException ex){
			throw new MessageRuntimeException(ex);
		}
	}
	/**
	 * 释放内存
	 * @param b
	 */
	private static void release(ByteBuffer b){
		try {
			Object obj = invoke(b,"cleaner");
			invoke(obj,"clean");
		} catch (Exception e) {
			logger.info(e);
			throw new MessageRuntimeException("release buffer failed!!");
		}
	}
	private static Object invoke(final Object target,String methodName) throws Exception{
		final Method method = target.getClass().getMethod(methodName, new Class<?>[0]);
		return AccessController.doPrivileged(new PrivilegedAction<Object>() {
			@Override
			public Object run() {
				try {
					return method.invoke(target);
				} catch (Exception e) {
					logger.info(e);
					throw new MessageRuntimeException("release buffer failed!!");
				}
			}
		});
	}
	private static String getNewPath(String oldPath,String newName){
		return oldPath.substring(0,oldPath.lastIndexOf('/'))+newName+getExt(oldPath);
	}
	private static String getExt(String path){
		return path.substring(path.lastIndexOf('.'));
	}
}
